/*
 * GeoManager.cpp
 *
 *  Created on: 14 Oct 2011
 *      Author: allan
 */

#include "GeoManager.h"

// Boost includes
#include <boost/foreach.hpp>

// GeoReact includes
#include "Core/Multiphase.h"
#include "Core/Phase.h"
#include "Core/ReactionSystem.h"

#include "Geochemistry/GeoDatabase.h"
#include "Geochemistry/GeoSystem.h"
#include "Geochemistry/Activity/AqueousActivity.h"
#include "Geochemistry/Activity/GaseousActivity.h"
#include "Geochemistry/Activity/MineralActivity.h"
#include "Geochemistry/Kinetics/MineralKinetics/MineralKinetics.h"
#include "Geochemistry/Utils/Assembler.h"

#include "Utils/Algorithms.h"
#include "Utils/SpeciesUtils.h"

GeoManager::GeoManager()
{}

void GeoManager::AddAqueousSpecies(const string& species)
{
	aqueousSpecies.push_back(species);
}

void GeoManager::AddGaseousSpecies(const string& species)
{
	gaseousSpecies.push_back(species);
}

void GeoManager::AddMineralSpecies(const string& species)
{
	mineralSpecies.push_back(species);
}

void GeoManager::SetAqueousSpecies(const vector<string>& aqueousSpecies)
{
	this->aqueousSpecies = aqueousSpecies;
}

void GeoManager::SetGaseousSpecies(const vector<string>& gaseousSpecies)
{
	this->gaseousSpecies = gaseousSpecies;
}

void GeoManager::SetMineralSpecies(const vector<string>& mineralSpecies)
{
	this->mineralSpecies = mineralSpecies;
}

void GeoManager::SpeciateAqueousSpecies(const vector<string>& components)
{
	aqueousSpecies = AssembleAqueousSpecies(components);
}

void GeoManager::SpeciateGaseousSpecies(const vector<string>& components)
{
	gaseousSpecies = AssembleGaseousSpecies(components);
}

void GeoManager::SpeciateMineralSpecies(const vector<string>& components)
{
	mineralSpecies = AssembleMineralSpecies(components);
}

void GeoManager::SetActivityCoefficient(const string& species, const ActivityCoefficient& activityCoefficient)
{
	activityCoefficients[species] = activityCoefficient;
}

void GeoManager::SetMineralKinetics(const string& mineral, const MineralKineticsInfo& mineralKineticsInfo)
{
	mineralKineticsInfos[mineral] = mineralKineticsInfo;
	
	kSpecies.push_back(mineral);
}

const vector<string> GeoManager::GetAqueousSpecies() const
{
	return aqueousSpecies;
}

const vector<string> GeoManager::GetGaseousSpecies() const
{
	return gaseousSpecies;
}

const vector<string> GeoManager::GetMineralSpecies() const
{
	return mineralSpecies;
}

const Phase GeoManager::AssembleAqueousPhase(const vector<string>& species) const
{
	AqueousActivity activity(species);
		
	BOOST_FOREACH(auto pair, activityCoefficients)
		activity.SetActivityCoefficient(pair.first, pair.second);
		
	return Phase("Aqueous", species, activity);
}

const Phase GeoManager::AssembleGaseousPhase(const vector<string>& species) const
{
	GaseousActivity activity(species);
		
	BOOST_FOREACH(auto pair, activityCoefficients)
		activity.SetActivityCoefficient(pair.first, pair.second);
		
	return Phase("Gaseous", species, activity);
}

const Phase GeoManager::AssembleMineralPhase(const vector<string>& species) const
{
	MineralActivity activity(species);
		
	BOOST_FOREACH(auto pair, activityCoefficients)
		activity.SetActivityCoefficient(pair.first, pair.second);
	
	// Build the name of the solid phase by appending each mineral that compose it
	string phaseName = "";
	
	BOOST_FOREACH(auto s, species)
	{ phaseName.append(s); phaseName.append("-"); }
	
	phaseName.erase(phaseName.size()-1);
	
	return Phase(phaseName, species, activity);
}

const Multiphase GeoManager::AssembleMultiphase() const
{
	vector<Phase> phases;
	
	if(!aqueousSpecies.empty())
		phases.push_back(AssembleAqueousPhase(aqueousSpecies));
	
	if(!gaseousSpecies.empty())
		phases.push_back(AssembleGaseousPhase(gaseousSpecies));
	
	BOOST_FOREACH(const string& mineral, mineralSpecies)
		phases.push_back(AssembleMineralPhase({mineral}));
	
	return Multiphase(phases);
}

const ReactionSystem GeoManager::AssembleEquilibriumReactions(const Multiphase& multiphase) const
{
	const vector<string> eSpecies = Difference(FilterSecondarySpecies(multiphase.GetSpecies()), kSpecies);
	
	vector<Reaction> reactions;
	
	BOOST_FOREACH(const string& reactant, eSpecies)
		reactions.push_back(AssembleReaction(reactant, multiphase));
	
	return ReactionSystem(reactions);
}

const ReactionSystem GeoManager::AssembleKineticReactions(const Multiphase& multiphase) const
{
	vector<Reaction> reactions;
	
	BOOST_FOREACH(const string& reactant, kSpecies)
	{
		Reaction reaction = AssembleReaction(reactant, multiphase);
		
		if(mineralKineticsInfos.count(reactant))
		{
			MineralKineticsInfo mkinfo = mineralKineticsInfos.find(reactant)->second;
			
			MineralKinetics mk(mkinfo, multiphase);
			
			reaction.SetKinetics(mk);
		}
		
		reactions.push_back(reaction);
	}
	
	return ReactionSystem(reactions);
}

const GeoSystem GeoManager::AssembleGeoSystem() const
{
	Multiphase multiphase = AssembleMultiphase();
	
	ReactionSystem eReactions = AssembleEquilibriumReactions(multiphase);
	
	ReactionSystem kReactions = AssembleKineticReactions(multiphase);
	
	GeoSystem geosystem(multiphase, eReactions, kReactions);
	
	geosystem.SetSpeciesPartition(FilterBasisSpecies(multiphase.GetSpecies()), kSpecies);
	
	return geosystem;
}
